-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local bs = require 'mc.bitstring'
local log = require 'mc.logging'
local libmgmt_protocol = require 'libmgmt_protocol'
local utils = require 'mc.utils'

-- SDI BlackBox
local Hi1822_pldm = {
    protocol_dependencies = { pldm_huawei = { endpoint = nil } },
    properties = {
        BlackBox = {
            protocol = 'pldm_huawei',
            action = 'on_demand',
            request = {
                -- 运行时需传入data(包括offset length)
                command_code = 0x03,
                huawei_cmd_id = 0x03,
                sub_cmd_id = 0x07,
                extra_cmd = 24
            },
            response = function(data)
                local r = bs.new([[<<
                    _:8,
                    total_length:32/big,
                    length:32/big,
                    data:1024/string
                >>]]):unpack(data, true)
                return {
                    total_length = r.total_length,
                    length = r.length,
                    data = r.data
                }
            end
        }
    }
}

-- SP580
local Hi1822 = {
    protocol_dependencies = { smbus = { ref_chip = nil, buffer_len = 64 } },
    properties = {
        Health = {
            protocol = 'smbus',
            action = 'on_schedule',
            period_in_sec = 30,
            request = { opcode = 0x1, expect_data_len = 1 },
            response = function(data)
                return bs.new([[<<health:8>>]]):unpack(data, true).health
            end
        },
        FaultCode = {
            protocol = 'smbus',
            action = 'on_schedule',
            period_in_sec = 5,
            request = { opcode = 0x2 },
            response = libmgmt_protocol.create_array_parser([[<<code:16>>]], 2)
        },
        ChipTemp = {
            protocol = 'smbus',
            action = 'on_schedule',
            period_in_sec = 5,
            request = { opcode = 0x3, expect_data_len = 2 },
            response = function(data)
                return bs.new([[<<temp:16/signed>>]]):unpack(data, true).temp
            end
        },
        ErrorLog = {
            protocol = 'smbus',
            action = 'on_demand',
            request = { opcode = 0xC },
            response = function(data)
                return data
            end
        },
        LastWord = {
            protocol = 'smbus',
            action = 'on_demand',
            request = { opcode = 0xD },
            response = function(data)
                return data
            end
        },
        RunningLog = {
            protocol = 'smbus',
            action = 'on_demand',
            request = { opcode = 0xE },
            response = function(data)
                return data
            end
        },
        FirmwareVersion = {
            protocol = 'smbus',
            action = 'on_schedule',
            period_in_sec = 120,
            request = { opcode = 0x5, expect_data_len = 3 },
            response = function(data)
                local r = bs.new([[<<
                    buf1:8,
                    buf2:8,
                    buf3:8
                >>]]):unpack(data)
                return string.format('%s.%s.%s.%s', r.buf1, r.buf2 // 10, r.buf2 % 10, r.buf3)
            end
        },
        OpticalTemp = {
            protocol = 'smbus',
            action = 'on_schedule',
            period_in_sec = 5,
            request = { opcode = 0x400 },
            response = function(data)
                local parser = libmgmt_protocol.create_array_parser([[<<temp:16>>]], 2)
                local r = parser(data)
                if not r then
                    return r
                end
                local result = {}
                local temp
                for i, t in ipairs(r) do
                    temp = t.temp
                    if temp == 0x7ffd then
                        log:debug('optical temp for port %d is invalid', i)
                        result[i] = 0xFFFF
                    elseif temp == 0x7ffe then
                        log:debug('optical module is not available for port %d', i)
                        result[i] = 0xFFFF
                    elseif temp == 0x7fff then
                        log:debug('Failed to read optical temp from port %d', i)
                        result[i] = 0xFFFF
                    else
                        result[i] = temp
                    end
                end
                return result
            end
        },
        LinkStatus = {
            protocol = 'smbus',
            action = 'on_schedule',
            period_in_sec = 5,
            request = { opcode = 0x403 },
            response = function(data)
                local parser = libmgmt_protocol.create_array_parser([[<<link:8>>]], 1)
                local r = parser(data)
                if not r then
                    return r
                end
                local link_switch <const> = { [0x0] = 'Disconnected', [0x1] = 'Connected', [0xFF] = 'N/A' }
                local result = {}
                for _, d in ipairs(r) do
                    result[#result + 1] = link_switch[d.link] or 'N/A'
                end
                return result
            end
        },
        MacAddress = {
            protocol = 'smbus',
            action = 'on_schedule',
            period_in_sec = 10,
            request = { opcode = 0x404 },
            response = function(data)
                local parser = libmgmt_protocol.create_array_parser(
                    [[<<port:8, PF_VF:16, addr:1/MAC_ADDRESS>>]], 9)
                local r = parser(data)
                if not r then
                    return r
                end
                local result = {}
                for _, d in ipairs(r) do
                    result[#result + 1] = d.addr.mac_address
                end
                return result
            end
        },
        PHYTemp = {
            protocol = 'smbus',
            action = 'on_demand',
            request = { opcode = 0x405 },
            response = function(data)
                local temp = bs.new([[<<temp:16>>]]):unpack(data).temp
                if temp == 0x7ffd then
                    log:error('PHY temp is invalid')
                elseif temp == 0x7ffe then
                    log:warn('PHY chip is not initialized')
                elseif temp == 0x7fff then
                    log:error('Failed to read PHY temp')
                else
                    return temp
                end
            end
        }
    }
}

local ncsi_vender_rsp = [[<<
    response_code:16/big,
    reason_code:16/big,
    ncsi_version_major:8,
    ncsi_version_minor:8,
    ncsi_version_update:8,
    ncsi_version_alpha1:8,
    rsvd0:24,
    ncsi_version_alpha2:8,
    firmware_name:12/string,
    firmware_version_0:8,
    firmware_version_1:8,
    firmware_version_2:8,
    firmware_version_3:8,
    pci_did:16/big,
    pci_vid:16/big,
    pci_ssid:16/big,
    pci_svid:16/big,
    manufacturer_id:32/big
    >>]]

local ncsi_port_metrics_rsp = [[<<
    response_code:16/big,
    reason_code:16/big,
    counters_cleared_from_last_read_ms:32/big,
    counters_cleared_from_last_read_ls:32/big,
    total_bytes_received:64/big,
    total_bytes_transmitted:64/big,
    total_unicast_packets_received:64/big,
    total_multicast_packets_received:64/big,
    total_broadcast_packets_received:64/big,
    total_unicast_packets_transmitted:64/big,
    total_multicast_packets_transmitted:64/big,
    total_broadcast_packets_transmitted:64/big,
    fcs_received_errors:32/big,
    alignment_errors:32/big,
    false_carrier_detections:32/big,
    runt_packets_received:32/big,
    jabber_packets_received:32/big,
    pause_xon_frames_received:32/big,
    pause_xoff_frames_received:32/big,
    pause_xon_frames_transmitted:32/big,
    pause_xoff_frames_transmitted:32/big,
    single_collision_transmit_frames:32/big,
    multiple_collision_transmit_frames:32/big,
    late_collision_frames:32/big,
    excessive_collision_frames:32/big,
    control_frames_received:32/big,
    byte_64_frames_received:32/big,
    byte_65_127_frames_received:32/big,
    byte_128_255_frames_received:32/big,
    byte_256_511_frames_received:32/big,
    byte_512_1023_frames_received:32/big,
    byte_1024_1522_frames_received:32/big,
    byte_1523_9022_frames_received:32/big,
    byte_64_frames_transmitted:32/big,
    byte_65_127_frames_transmitted:32/big,
    byte_128_255_frames_transmitted:32/big,
    byte_256_511_frames_transmitted:32/big,
    byte_512_1023_frames_transmitted:32/big,
    byte_1024_1522_frames_transmitted:32/big,
    byte_1523_9022_frames_transmitted:32/big,
    valid_bytes_received:64/big,
    error_runt_packets_received:32/big,
    error_jabber_packets_received:32/big
    >>]]

local ncsi_linkstatus_rsp =[[<<
    response_code:16/big,
    reason_code:16/big,

    # link status 有4个字节，大字节序
    # link status 第1个字节
    extended_speed_duplex:8,

    # link status 第二个字节
    tx_flow_control:1,
    rx_flow_control:1,
    link_partner8:2,
    serdes_link:1,
    oem_link_speed:1,
    rsvd1:2,

    # link status 第三个字节
    rsvd2:1,
    link_partner1:1,
    link_partner2:1,
    link_partner3:1,
    link_partner4:1,
    link_partner5:1,
    link_partner6:1,
    link_partner7:1,

    # link status 第四个字节
    link_flag:1,
    speed_duplex:4,
    negotiate_flag:1,
    negotiate_complete:1,
    parallel_detection:1,

    # other indication 有4个字节
    rsvd3:24,
    host_nc_driver_status_indication:1,
    rsvd4:7,

    oem_link_status:32/big
    >>]]

local ncsi_opticalmodule_info_rsp = [[<<
    _:8,
    device_part_number:16/string,
    device_vendor:16/string,
    device_serial_number:16/string,
    device_identifier:8,
    device_type:8,
    device_connector:8,
    _:8,
    device_transfer_distance:16/big,
    device_wavelength:16/big,
    device_working_param_temp:16/big,
    device_working_param_voltage:16/big,
    device_working_param_tx_bias_current:16/big,
    device_working_param_tx_power:16/big,
    device_working_param_rx_power:16/big,
    device_warning_threshold_low_temp:16/big,
    device_warning_threshold_high_temp:16/big,
    device_warning_threshold_tx_power:16/big,
    device_warning_threshold_rx_power:16/big,
    device_alarm_threshold_low_temp:16/big,
    device_alarm_threshold_high_temp:16/big,
    device_alarm_threshold_tx_power:16/big,
    device_alarm_threshold_rx_power:16/big,
    rx_loss_state,
    tx_fault_state
    >>]]
local identifier_switch <const> = {
    [0x00] = 'UNDEF',
    [0x01] = 'GBIC',
    [0x03] = 'SFP_SFP_PLUS_SFP28',
    [0x0c] = 'QSFP',
    [0x0d] = 'QSFP_PLUS',
    [0x11] = 'QSFP28'
}
local connector_switch <const> = {
    [0x00] = 'UNDEF',
    [0X07] = 'LC',
    [0x0c] = 'MPO',
    [0x0b] = 'OPTICAL_PIGTAIL',
    [0x21] = 'COPPER_PIGTAIL',
    [0x23] = 'NO_SEPARABLE_CONNECTOR'
}
local device_type_switch <const> = {
    [0x01] = 'Optical',
    [0x02] = 'Electric',
    [0x03] = 'Copper',
    [0x04] = 'Aoc',
    [0x05] = 'Interface',
    [0x06] = 'Baset'
}

local micro_to_milli <const> = 1000

local function convert_micro_to_milli(v)
    if v == 0xFFFF then
        return v
    end
    return v / micro_to_milli
end

local function trim(s)
    return s:match '^%s*(.*)':match '(.-)%s*$'
end

local Hi1822_ncsi = {
    protocol_dependencies = { ncsi_standard = { endpoint = nil }, ncsi_huawei = { endpoint = nil } },
    properties = {
        Test = {
            protocol = 'ncsi_standard',
            action = 'on_demand',
            request = {},
            response = function(data)
                return data
            end
        },
        InitializeNCSIChannel = {
            protocol = 'ncsi_standard',
            action = 'on_demand',
            request = {
                -- 运行时需传入channel_id(也就是port id)
                packet_type = 0x00,
                expect_rsp_packet_type = 0x80
            },
            response = function(data)
                return bs.new([[<<
                    response_code:16/big,
                    reason_code:16/big
                >>]]):unpack(data, true)
            end
        },
        VendorID = {
            protocol = 'ncsi_standard',
            action = 'on_schedule',
            period_in_sec = 120,
            request = {
                packet_type = 0x15,
                expect_rsp_packet_type = 0x95,
                channel_id = 0 -- 默认跟port0获取
            },
            response = function(data)
                local r = bs.new(ncsi_vender_rsp):unpack(data, true)
                local bsd_to_string = function(v)
                    if v & 0xF0 == 0xF0 then
                        return tostring(v)
                    elseif v < 0x10 then
                        return '0' .. tostring(v)
                    end
                    return tostring((v // 16) * 10)
                end
                local ncsi_version = bsd_to_string(r.ncsi_version_major) ..
                    bsd_to_string(r.ncsi_version_minor)
                if r.ncsi_version_update ~= 0xFF then
                    ncsi_version = ncsi_version .. bsd_to_string(r.ncsi_version_update)
                end
                ncsi_version = ncsi_version .. string.char(r.ncsi_version_alpha1) ..
                    string.char(r.ncsi_version_alpha2)
                local firmware_version = string.format('%d.%d.%d.%d',
                    r.firmware_version_0, r.firmware_version_1, r.firmware_version_2,
                    r.firmware_version_3)
                log:debug('get firmware by ncsi over mctp, value is %s', firmware_version)
                return {
                    FirmwareVersion = firmware_version,
                    VendorID = string.format('0x%04x', r.pci_vid),
                    DeviceID = string.format('0x%04x', r.pci_did),
                    SubsystemVendorID = string.format('0x%04x', r.pci_svid),
                    SubsystemDeviceID = string.format('0x%04x', r.pci_ssid)
                }
            end
        },
        PortMetrics = {
            protocol = 'ncsi_standard',
            action = 'on_schedule',
            period_in_sec = 60,
            request = {
                -- 运行时需传入channel_id(也就是port id)
                packet_type = 0x18,
                expect_rsp_packet_type = 0x98
            },
            response = function(data)
                local r = bs.new(ncsi_port_metrics_rsp):unpack(data, true)
                return {
                    RXFrames = r.total_bytes_received,
                    TXFrames = r.total_bytes_transmitted,
                    RXUnicastFrames = r.total_unicast_packets_received,
                    RXMulticastFrames = r.total_multicast_packets_received,
                    RXBroadcastFrames = r.total_broadcast_packets_received,
                    TXUnicastFrames = r.total_unicast_packets_transmitted,
                    TXMulticastFrames = r.total_multicast_packets_transmitted,
                    TXBroadcastFrames = r.total_broadcast_packets_transmitted,
                    RXFCSErrors = r.fcs_received_errors,
                    RXUndersizeFrames = r.runt_packets_received,
                    RXOversizeFrames = r.jabber_packets_received,
                    TXSingleCollisions = r.single_collision_transmit_frames,
                    TXMultipleCollisions = r.multiple_collision_transmit_frames,
                    TXLateCollisions = r.late_collision_frames,
                    TXExcessiveCollisions = r.excessive_collision_frames,
                    RXFrameAlignmentErrors = r.alignment_errors
                }
            end
        },
        LinkStatus = {
            protocol = 'ncsi_standard',
            action = 'on_schedule',
            period_in_sec = 5,
            request = {
                -- 运行时需传入channel_id(也就是port id)
                packet_type = 0x0A,
                expect_rsp_packet_type = 0x8A
            },
            response = function(data)
                local r = bs.new(ncsi_linkstatus_rsp):unpack(data, true)

                local speed <const> = {
                    10, 10, 100, 100, 100, 1000, 1000, 10000, 20000, 25000, 40000, 50000, 100000,
                    2500, 0, 1000, 200000, 400000, 800000
                }
                local full_duplex_flag <const> = {
                    false, true, false, false, true, false, true, true, true, true, true, true,
                    true, true, false, false
                }
                local link_switch <const> = { [0x0] = 'Disconnected', [0x1] = 'Connected', [0xFF] = 'N/A' }

                local speed_mps = speed[r.speed_duplex] or 0
                if r.speed_duplex == 0xF then
                    speed_mps = speed[r.extended_speed_duplex] or 0
                end

                log:debug('link_flag %s', r.link_flag)
                return {
                    LinkStatus = link_switch[r.link_flag] or 'N/A',
                    SpeedMbps = speed_mps,
                    AutoSpeedNegotiation = r.negotiate_flag == 1,
                    FullDuplex = full_duplex_flag[r.speed_duplex] or false
                }
            end
        },
        BDF = {
            protocol = 'ncsi_huawei',
            action = 'on_demand',
            request = {
                -- 运行时需传入channel_id(也就是port id)和extra_cmd(也是PF Index)
                -- In single function mode, PF Index indicates Port_ID; In multi-PFs mode, PF Index indicates PF_ID
                huawei_cmd_id = 0x00,
                sub_cmd_id = 0x01
            },
            response = function(data)
                local r = bs.new([[<<
                    _:16,
                    bus:8,
                    device:8,
                    func:8
                >>]]):unpack(data, true)
                return { bus = r.bus, device = r.device, func = r.func }
            end
        },
        ChipTemp = {
            protocol = 'ncsi_huawei',
            action = 'on_schedule',
            period_in_sec = 5,
            request = { huawei_cmd_id = 0x00, sub_cmd_id = 0x0A },
            response = function(data)
                local r = bs.new([[<<
                    _:8,
                    temp:16/big-signed
                >>]]):unpack(data, true)
                return r and r.temp or nil
            end
        },
        OpticalTemp = {
            protocol = 'ncsi_huawei',
            action = 'on_schedule',
            period_in_sec = 5,
            request = { huawei_cmd_id = 0x00, sub_cmd_id = 0x0B },
            response = function(data)
                local r = bs.new([[<<
                    _:8,
                    temp:16/big
                >>]]):unpack(data, true)
                if not r then
                    return nil
                end
                -- 与1822 smbus协议处理保持一致
                if r.temp == 0x7ffd then
                    log:debug('optical temp is invalid')
                elseif r.temp == 0x7ffe then
                    log:debug('optical module is not available')
                elseif r.temp == 0x7fff then
                    log:debug('Failed to read optical temp')
                else
                    return r.temp
                end
            end
        },
        LinkAbility = {
            protocol = 'ncsi_huawei',
            action = 'on_demand',
            request = { huawei_cmd_id = 0x00, sub_cmd_id = 0x05 },
            response = function(data)
                local r = bs.new([[<<
                    _:8,
                    link_width:8,
                    link_speed:8
                >>]]):unpack(data, true)
                local link_width_switch <const> = {
                    [0x01] = 'x1',
                    [0x02] = 'x2',
                    [0x04] = 'x4',
                    [0x08] = 'x8',
                    [0x10] = 'x16',
                    [0x20] = 'x32'
                }
                local link_speed_switch <const> = {
                    [0x01] = 'Gen1 (2.5GT/s)',
                    [0x02] = 'Gen2 (5.0GT/s)',
                    [0x03] = 'Gen3 (8.0GT/s)',
                    [0x04] = 'Gen4 (16.0GT/s)',
                    [0x05] = 'Gen5 (32.0GT/s)'
                }
                return {
                    link_width = link_width_switch[r.link_width] or '',
                    link_speed = link_speed_switch[r.link_speed] or 'N/A'
                }
            end
        },
        LinkInfo = {
            protocol = 'ncsi_huawei',
            action = 'on_demand',
            request = { huawei_cmd_id = 0x00, sub_cmd_id = 0x06 },
            response = function(data)
                local r = bs.new([[<<
                    _:8,
                    link_width:8,
                    link_speed:8
                >>]]):unpack(data, true)
                local link_width_status_switch <const> = {
                    [0x01] = 'x1',
                    [0x02] = 'x2',
                    [0x04] = 'x4',
                    [0x08] = 'x8',
                    [0x10] = 'x16',
                    [0x20] = 'x32'
                }
                local link_speed_status_switch <const> = {
                    [0x01] = 'Gen1 (2.5GT/s)',
                    [0x02] = 'Gen2 (5.0GT/s)',
                    [0x03] = 'Gen3 (8.0GT/s)',
                    [0x04] = 'Gen4 (16.0GT/s)',
                    [0x05] = 'Gen5 (32.0GT/s)'
                }
                return {
                    link_width = link_width_status_switch[r.link_width] or '',
                    link_speed = link_speed_status_switch[r.link_speed] or 'N/A'
                }
            end
        },
        DCBX = {
            protocol = 'ncsi_huawei',
            action = 'on_schedule',
            period_in_sec = 60,
            request = {
                -- 运行时需传入channel_id(也就是port id)
                huawei_cmd_id = 0x01,
                sub_cmd_id = 0x02,
                extra_cmd = 0x00 -- 必须设为0否则会覆盖返回值里的dcbx_status，导致获取失败
            },
            response = function(data)
                local r = bs.new([[<<
                    dcbx_status:8,
                    up2cos:8/string,
                    up_pgid:8/string,
                    pgpct:8/string,
                    strict:8/string,
                    pfcmap:8,
                    rsvd0:24
                >>]]):unpack(data, true)
                if r.dcbx_status ~= 0x00 then
                    log:debug('unable to retrieve dcbx info, status: %d', r.dcbx_status)
                    return
                end
                local result = {
                    Pfcmap = r.pfcmap,
                    Up2cos = {},
                    Uppgid = {},
                    Pgpct = {},
                    PgStrict = {}
                }
                r.up2cos:gsub('.', function(v)
                    table.insert(result.Up2cos, string.byte(v))
                end)
                r.up_pgid:gsub('.', function(v)
                    table.insert(result.Uppgid, string.byte(v))
                end)
                r.pgpct:gsub('.', function(v)
                    table.insert(result.Pgpct, string.byte(v))
                end)
                r.strict:gsub('.', function(v)
                    table.insert(result.PgStrict, string.byte(v))
                end)
                return result
            end
        },
        MacAddrNCSI = {
            protocol = 'ncsi_huawei',
            action = 'on_schedule',
            period_in_sec = 10,
            request = {
                -- 运行时需传入channel_id(也就是port id), extra_cmd(也就是PortID or PF_index)
                huawei_cmd_id = 0x1,
                sub_cmd_id = 0x0
            },
            response = function(data)
                local r = bs.new([[<<
                    _:8,
                    mac_address_count:16/big,
                    _:16,
                    mac_addrs:8/MAC_ADDRESS
                >>]], libmgmt_protocol.common_bs_helper):unpack(data, true)

                if not r or #r.mac_addrs < 1 then
                    return nil
                end

                -- 存在多个mac地址，默认取第1个
                return r.mac_addrs[1].mac_address
            end
        },
        DefaultMacAddrNCSI = {
            protocol = 'ncsi_huawei',
            action = 'on_schedule',
            period_in_sec = 120,
            request = {
                -- 运行时需传入channel_id(也就是port id), extra_cmd(也就是PortID or PF_index)
                huawei_cmd_id = 0x1,
                sub_cmd_id = 0x4
            },
            response = function(data)
                local r = bs.new([[<<
                    _:8,
                    mac_addr_count:16/big,
                    _:16,
                    mac_addrs:8/MAC_ADDRESS
                >>]], libmgmt_protocol.common_bs_helper):unpack(data, true)
                if not r or #r.mac_addrs < 1 then
                    return nil
                end
                -- 存在多个mac地址，默认取第1个
                return r.mac_addrs[1].mac_address
            end
        },
        FaultStatCode = {
            protocol = 'ncsi_huawei',
            action = 'on_schedule',
            period_in_sec = 5,
            request = { huawei_cmd_id = 0x00, sub_cmd_id = 0x0C },
            response = function(data)
                local r = bs.new([[<<
                    _:8,
                    health_status:8,
                    error_code_count:8
                >>]]):unpack(data:sub(1, 3), true)
                local result = { health_status = r.health_status }
                if r.error_code_count == 0 then
                    return result
                end
                local parser = libmgmt_protocol.create_array_parser([[<<error_code:16/big>>]], 2)
                local codes = parser(data:sub(4))
                if not codes then
                    return result
                end
                result.error_codes = {}
                for i, v in ipairs(codes) do
                    result.error_codes[i] = v.error_code
                end
                return result
            end
        },
        SetOffLLDPCapability = {
            protocol = 'ncsi_huawei',
            action = 'on_demand',
            request = {
                -- 运行时需传入channel_id(也就是port id)
                huawei_cmd_id = 0x00,
                sub_cmd_id = 0x0E,
                extra_cmd = 0x0 -- 0为关
            },
            response = function(data)
                return true
            end
        },
        SetOnLLDPCapability = {
            protocol = 'ncsi_huawei',
            action = 'on_demand',
            request = {
                -- 运行时需传入channel_id(也就是port id)
                huawei_cmd_id = 0x00,
                sub_cmd_id = 0x0E,
                extra_cmd = 0x01 -- 1为开
            },
            response = function(data)
                return true
            end
        },
        GetLLDPCapability = {
            protocol = 'ncsi_huawei',
            action = 'on_schedule',
            period_in_sec = 120,
            request = {
                -- 运行时需传入channel_id(也就是port id)
                huawei_cmd_id = 0x00,
                sub_cmd_id = 0x0F
            },
            response = function(data)
                local r = bs.new([[<<
                    _:8,
                    enabled:8
                >>]]):unpack(data, true)
                return r.enabled == 0x01
            end
        },
        OpticalModuleInfo = {
            protocol = 'ncsi_huawei',
            action = 'on_schedule',
            period_in_sec = 10,
            request = {
                -- 运行时需传入channel_id(也就是port id), extra_cmd(也就是PortID or PF_index)
                huawei_cmd_id = 0x00,
                sub_cmd_id = 0x0D
            },
            response = function(data)
                local r = bs.new(ncsi_opticalmodule_info_rsp):unpack(data, true)
                local device_type = device_type_switch[r.device_type] or 'Unknown'
                -- 背板类型/电口卡不显示
                if device_type == 'Unknown' or device_type == 'Interface' or device_type == 'Baset' then
                    return {}
                end

                local result = {
                    PartNumber = trim(r.device_part_number),
                    Manufacturer = trim(r.device_vendor),
                    SerialNumber = trim(r.device_serial_number),
                    MediumType = device_type,
                    ConnectorType = connector_switch[r.device_connector] or '',
                    TransmissionDistance = tostring(r.device_transfer_distance), -- 资源树不知道为什么是string
                    RxLossState = (r.rx_loss_state == 1),                        -- rx_loss_state == 1 代表rx端收不到信号
                    TxFaultState = (r.tx_fault_state == 1),                      -- tx_fault_state == 1，代表tx端有错误
                    -- 光模块 electric info
                    TXBiasCurrentMilliAmps = {
                        convert_micro_to_milli(r.device_working_param_tx_bias_current)
                    },
                    TXOutputPowerMilliWatts = {
                        convert_micro_to_milli(r.device_working_param_tx_power)
                    },
                    RXInputPowerMilliWatts = {
                        convert_micro_to_milli(r.device_working_param_rx_power)
                    }
                }

                -- 以下各字段，FM当前版本还未支持获取，BMC获取到的是无效值属于正常现象
                if r.device_working_param_voltage ~= 0xFFFF then
                    result.SupplyVoltage = convert_micro_to_milli(r.device_working_param_voltage)
                end
                if r.device_alarm_threshold_tx_power ~= 0xFFFF then
                    result.Power_TXUpperThresholdCritical = convert_micro_to_milli(
                        r.device_alarm_threshold_tx_power)
                end
                if r.device_alarm_threshold_rx_power ~= 0xFFFF then
                    result.RXUpperThresholdCritical = convert_micro_to_milli(
                        r.device_alarm_threshold_rx_power)
                end

                if device_type == 'Optical' or device_type == 'Electric' or device_type == 'Aoc' then
                    -- 添加单位nm
                    result.WaveLengthNanometer = string.format('%dnm', r.device_wavelength)
                    result.Identifier = identifier_switch[r.device_identifier] or ''
                    if r.device_alarm_threshold_low_temp ~= 0xFFFF then
                        result.Temp_LowerThresholdCritical = r.device_alarm_threshold_low_temp
                        result.TemperatureLowerThresholdCritical = r.device_alarm_threshold_low_temp
                    end
                    if r.device_alarm_threshold_high_temp ~= 0xFFFF then
                        result.Temp_UpperThresholdCritical = r.device_alarm_threshold_high_temp
                        result.TemperatureUpperThresholdCritical = r.device_alarm_threshold_high_temp
                    end
                end
                return result
            end
        },
        GetLog = {
            protocol = 'ncsi_huawei',
            action = 'on_demand',
            request = {
                -- 运行时需传入subcmd_id(新老固件cmd不同)data(包括offset length)和extra_cmd(log type)
                channel_id = 0x00,
                huawei_cmd_id = 0x00
            },
            response = function(data)
                local r = bs.new([[<<
                    _:8,
                    data:1024/string
                >>]]):unpack(data, true)
                return r.data
            end
        },
        GetNewLog = {
            protocol = 'ncsi_huawei',
            action = 'on_demand',
            request = {
                -- 调用时需要传入data和extra_cmd(log type)
                channel_id = 0x00,
                huawei_cmd_id = 0x00,
                sub_cmd_id = 0x16
            },
            response = function(data)
                local r = bs.new([[<<
                    _:8,
                    data/string
                >>]]):unpack(data, true)
                return r.data
            end
        }
    }
}

return {
    smbus = function(ref_chip)
        local obj = utils.table_copy(Hi1822)
        obj.protocol_dependencies.smbus.ref_chip = ref_chip
        return obj
    end,
    mctp = function(endpoint)
        local obj = utils.table_copy(Hi1822_ncsi)
        obj.protocol_dependencies.ncsi_huawei.endpoint = endpoint
        obj.protocol_dependencies.ncsi_standard.endpoint = endpoint
        return obj
    end,
    mctp_pldm = function(endpoint)
        local obj = utils.table_copy(Hi1822_pldm)
        obj.protocol_dependencies.pldm_huawei.endpoint = endpoint
        return obj
    end,
    lldp = function()
    end
}
